#if !defined(PROFILE_H)
#define PROFILE_H 

#include <string>
#include <map>
#include <sstream>
#include "new_exception.h"

class profile 
{
	struct value {
		value() : read_once(false) {}
		value(const std::string & s) : read_once(false), str(s) {}
		mutable bool read_once;
		std::string str;
	};
	typedef std::map<std::string, value> items_t;
	items_t items;
	bool m_use_exceptions;

private:
	void trim(std::string & s) const;
	void init(const char *filename);
	void init(std::istream & in);

public:
	profile(std::istream & in, bool use_exceptions = false);
	profile(const char * filename, bool use_exceptions = false);
	~profile();
	bool use_exceptions() const
		{ return m_use_exceptions; }
	void use_exceptions(bool new_val)
		{ m_use_exceptions = new_val; }
	template <class T> bool get(const char * name, T & value) const; 
	#if defined _MSC_VER && !defined __INTEL_COMPILER
		template <> bool get(const char * name, std::string & value) const;
	#endif
	template <class T> bool get(const std::string & name, T & value) const 
		{ return get(name.c_str(), value); }
	friend std::ostream & operator << (std::ostream & s, const profile & p);
	///if unused params found return true also if not use_exceptions (otherwise throws)
	///\param name will contain the name of first unused parameter
	bool has_unused_params(std::string * name = 0) const;

public:
	EXCEPTION( CantOpenFile,	"Can't open profile file!" )
	EXCEPTION( CantGet,			"Can't get parameter " )
	EXCEPTION( ParseError,		"Parameter parse error: " )
	EXCEPTION( CantInclude,		"'#include' directive parse error!" )
	EXCEPTION( Unexpected,		"Unexpected parameter " )
};

template <class T> 
bool profile::get(const char * name, T & value) const {
	std::string n(name);
	trim(n);
	bool good, e_parse;
	items_t::const_iterator i = items.find(n);
	if (i != items.end()) {
		i->second.read_once = true;
		const std::string & v = i->second.str;
		std::istringstream in(v);
		in.exceptions(std::ios_base::iostate(0));
		in >> value;
		bool begin_ok = !in.fail() && !in.bad();
		good = begin_ok && in.eof();
		e_parse = begin_ok && !good;
	} else {
		good = false;
		e_parse = false;
	}
	if (m_use_exceptions && e_parse) 
		throw ParseError().append(name);
	if (m_use_exceptions && !good) 
		throw CantGet().append(name);
	return good;
}

template <>
inline bool profile::get(const char * name, std::string & value) const {
	std::string n(name);
	trim(n);
	items_t::const_iterator it;
	if ((it = items.find(n)) != items.end()) {
		it->second.read_once = true;
		value = (* it).second.str;
		return true;
	} else {
		if (m_use_exceptions) 
			throw CantGet().append(name); 
		return false;
	}
}


#endif // PROFILE_H
